﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using LunchCommand.Data;
using LunchCommand.Models;
using LunchCommand.Services;
using LunchCommand.Models.Logging;
namespace Tests.LunchServiceTests
{
    [TestClass]
    public class VenueFilterTests : BaseLunchServiceTest
    {
        [TestMethod]
        public void FiltersOutQuick()
        {
            this.LikeAllOfThem(this.UserAlpha);

            this.PizzaPlace.IsQuick = true;
            this.SushiBar.IsQuick = true;

            this.DatabaseContext.SaveChanges();

            var result = this.Service.GetPossibleVenues(this.DatabaseContext.ActiveVenues, false, new[] { this.UserAlpha });

            Assert.IsFalse(result.Venues.Contains(this.PizzaPlace));
            Assert.IsFalse(result.Venues.Contains(this.SushiBar));
            Assert.IsTrue(result.Venues.Contains(this.WaffleHouse));
            Assert.IsTrue(result.Venues.Contains(this.SlopShop));
            Assert.IsTrue(result.Venues.Contains(this.Tacos));
        }

        [TestMethod]
        public void FiltersOutSlow()
        {
            this.LikeAllOfThem(this.UserAlpha);

            this.PizzaPlace.IsQuick = true;
            this.SushiBar.IsQuick = true;

            this.DatabaseContext.SaveChanges();

            var result = this.Service.GetPossibleVenues(this.DatabaseContext.ActiveVenues, true, new[] { this.UserAlpha });

            Assert.IsTrue(result.Venues.Contains(this.PizzaPlace));
            Assert.IsTrue(result.Venues.Contains(this.SushiBar));
            Assert.IsFalse(result.Venues.Contains(this.WaffleHouse));
            Assert.IsFalse(result.Venues.Contains(this.SlopShop));
            Assert.IsFalse(result.Venues.Contains(this.Tacos));
        }

        [TestMethod]
        public void DontFiltersOutEither()
        {
            this.LikeAllOfThem(this.UserAlpha);

            this.PizzaPlace.IsQuick = true;
            this.SushiBar.IsQuick = true;

            this.DatabaseContext.SaveChanges();

            var result = this.Service.GetPossibleVenues(this.DatabaseContext.ActiveVenues, null, new[] { this.UserAlpha });

            Assert.IsTrue(result.Venues.Contains(this.PizzaPlace));
            Assert.IsTrue(result.Venues.Contains(this.SushiBar));
            Assert.IsTrue(result.Venues.Contains(this.WaffleHouse));
            Assert.IsTrue(result.Venues.Contains(this.SlopShop));
            Assert.IsTrue(result.Venues.Contains(this.Tacos));
        }

        [TestMethod]
        public void FilterOutThoseSlowOnesVisitedTooRecently()
        {
            this.LikeAllOfThem(this.UserAlpha);

            this.PizzaPlace.IsQuick = true;
            this.SushiBar.IsQuick = true;

            this.DatabaseContext.Lunches.Add(new Lunch()
            {
                Venue = this.WaffleHouse,
                LunchTime = DateTime.Now.AddDays(-3)
            });

            this.DatabaseContext.Lunches.Add(new Lunch()
            {
                Venue = this.SlopShop,
                LunchTime = DateTime.Now.AddDays(-6.5)
            });

            this.DatabaseContext.Lunches.Add(new Lunch()
            {
                Venue = this.Tacos,
                LunchTime = DateTime.Now.AddDays(-7.1)
            });

            this.DatabaseContext.Lunches.Add(new Lunch()
            {
                QuickVenue = this.Tacos,
                LunchTime = DateTime.Now.AddDays(-1.1)
            });

            this.DatabaseContext.Lunches.Add(new Lunch()
            {
                QuickVenue = this.SushiBar,
                LunchTime = DateTime.Now.AddDays(-8.1)
            });

            this.DatabaseContext.SaveChanges();

            var result = this.Service.GetPossibleVenues(this.DatabaseContext.ActiveVenues, false, new[] { this.UserAlpha });

            Assert.IsFalse(result.Venues.Contains(this.PizzaPlace));
            Assert.IsFalse(result.Venues.Contains(this.SushiBar));
            Assert.IsFalse(result.Venues.Contains(this.WaffleHouse));
            Assert.IsFalse(result.Venues.Contains(this.SlopShop));
            Assert.IsTrue(result.Venues.Contains(this.Tacos));

            result = this.Service.GetPossibleVenues(this.DatabaseContext.ActiveVenues, null, new[] { this.UserAlpha });

            Assert.IsTrue(result.Venues.Contains(this.PizzaPlace));
            Assert.IsTrue(result.Venues.Contains(this.SushiBar));
            Assert.IsFalse(result.Venues.Contains(this.WaffleHouse));
            Assert.IsFalse(result.Venues.Contains(this.SlopShop));
            Assert.IsTrue(result.Venues.Contains(this.Tacos));
        }

        [TestMethod]
        public void FilterOutThoseQuickOnesVisitedTooRecently()
        {
            this.LikeAllOfThem(this.UserAlpha);

            this.SlopShop.IsQuick = true;
            this.Tacos.IsQuick = true;
            this.WaffleHouse.IsQuick = true;
            this.PizzaPlace.IsQuick = true;
            this.BurgerJoint.IsQuick = true;

            this.DatabaseContext.Lunches.Add(new Lunch()
            {
                Venue = this.WaffleHouse,
                LunchTime = DateTime.Now.AddDays(-3)
            });

            this.DatabaseContext.Lunches.Add(new Lunch()
            {
                QuickVenue = this.SlopShop,
                LunchTime = DateTime.Now.AddDays(-6.5)
            });

            this.DatabaseContext.Lunches.Add(new Lunch()
            {
                QuickVenue = this.Tacos,
                LunchTime = DateTime.Now.AddDays(-7.1)
            });

            this.DatabaseContext.Lunches.Add(new Lunch()
            {
                Venue = this.Tacos,
                LunchTime = DateTime.Now.AddDays(-1.1)
            });

            this.DatabaseContext.SaveChanges();

            var result = this.Service.GetPossibleVenues(this.DatabaseContext.ActiveVenues, true, new[] { this.UserAlpha });

            Assert.IsTrue(result.Venues.Contains(this.PizzaPlace));
            Assert.IsFalse(result.Venues.Contains(this.SushiBar));
            Assert.IsTrue(result.Venues.Contains(this.WaffleHouse));
            Assert.IsFalse(result.Venues.Contains(this.SlopShop));
            Assert.IsTrue(result.Venues.Contains(this.Tacos));
        }

        [TestMethod]
        public void FiltersOutThoseLimitedToDayAndAlsoDeletedOnes()
        {
            this.LikeAllOfThem(this.UserAlpha);

            this.WaffleHouse.Deleted = true;
            this.PizzaPlace.DaysOfWeek = new VenueDaysOfWeek();
            var dow = this.PizzaPlace.DaysOfWeek = new VenueDaysOfWeek();

            typeof(VenueDaysOfWeek).GetProperty(Enum.GetName(typeof(DayOfWeek), DateTime.Now.DayOfWeek)).SetValue(dow, false);

            this.DatabaseContext.SaveChanges();

            var result = this.Service.GetPossibleVenues(this.DatabaseContext.ActiveVenues, null, new[] { this.UserAlpha });

            Assert.IsFalse(result.Venues.Contains(this.PizzaPlace));
            Assert.IsTrue(result.Venues.Contains(this.SushiBar));
            Assert.IsFalse(result.Venues.Contains(this.WaffleHouse));
            Assert.IsTrue(result.Venues.Contains(this.SlopShop));
            Assert.IsTrue(result.Venues.Contains(this.Tacos));
        }

        [TestMethod]
        public void FilterThoseHated()
        {
            var venues = new[] { this.WaffleHouse, this.Tacos, this.PizzaPlace };
            var users = new[] { this.UserAlpha, this.UserBravo, this.UserCharlie };

            this.UserAlpha.VenueDecisions.Add(new UserVenueChoice() { User = this.UserAlpha, Venue = this.WaffleHouse, Degree = DegreeOfPreference.WantToGo });
            this.UserBravo.VenueDecisions.Add(new UserVenueChoice() { User = this.UserBravo, Venue = this.Tacos, Degree = DegreeOfPreference.WantToGo });
            this.UserCharlie.VenueDecisions.Add(new UserVenueChoice() { User = this.UserCharlie, Venue = this.WaffleHouse, Degree = DegreeOfPreference.WillNotGo });
            this.UserCharlie.VenueDecisions.Add(new UserVenueChoice() { User = this.UserCharlie, Venue = this.PizzaPlace, Degree = DegreeOfPreference.WantToGo });

            var result = this.Service.GetPossibleVenues(venues, false, users);

            Assert.IsTrue(result.Venues.Contains(this.PizzaPlace));
            Assert.IsTrue(result.Venues.Contains(this.Tacos));
            Assert.IsFalse(result.Venues.Contains(this.WaffleHouse));
            Assert.IsTrue(result.HappyUsers.Count() == 3);
        }

        [TestMethod]
        public void DropUsersWhoHateTooMuch()
        {
            var venues = new[] { this.WaffleHouse, this.Tacos, this.PizzaPlace };
            var users = new[] { this.UserAlpha, this.UserBravo, this.UserCharlie };

            this.UserAlpha.VenueDecisions.Add(new UserVenueChoice() { User = this.UserAlpha, Venue = this.WaffleHouse, Degree = DegreeOfPreference.WantToGo });
            this.UserBravo.VenueDecisions.Add(new UserVenueChoice() { User = this.UserBravo, Venue = this.Tacos, Degree = DegreeOfPreference.WantToGo });
            this.UserCharlie.VenueDecisions.Add(new UserVenueChoice() { User = this.UserCharlie, Venue = this.WaffleHouse, Degree = DegreeOfPreference.WillNotGo });
            this.UserCharlie.VenueDecisions.Add(new UserVenueChoice() { User = this.UserCharlie, Venue = this.PizzaPlace, Degree = DegreeOfPreference.WillNotGo});
            this.UserCharlie.VenueDecisions.Add(new UserVenueChoice() { User = this.UserCharlie, Venue = this.Tacos, Degree = DegreeOfPreference.WillNotGo });

            this.DatabaseContext.SaveChanges();

            var result = this.Service.GetPossibleVenues(venues, false, users);

            Assert.IsTrue(result.Venues.Contains(this.PizzaPlace));
            Assert.IsTrue(result.Venues.Contains(this.Tacos));
            Assert.IsTrue(result.Venues.Contains(this.WaffleHouse));
            Assert.IsFalse(result.HappyUsers.Contains(this.UserCharlie));
            Assert.IsTrue(result.HappyUsers.Contains(this.UserAlpha));
            Assert.IsTrue(result.HappyUsers.Contains(this.UserBravo));
            Assert.IsTrue(result.UnhappyUsers.Contains(this.UserCharlie));
            Assert.IsTrue(result.UnhappyUsers.Count() == 1);

            this.UserBravo.VenueDecisions.Add(new UserVenueChoice() { User = this.UserBravo, Venue = this.WaffleHouse, Degree = DegreeOfPreference.WillNotGo });
            this.UserBravo.VenueDecisions.Add(new UserVenueChoice() { User = this.UserBravo, Venue = this.PizzaPlace, Degree = DegreeOfPreference.WillNotGo});
            this.UserBravo.VenueDecisions.First(vd => vd.Venue == this.Tacos).Degree = DegreeOfPreference.WillNotGo;
            
            this.DatabaseContext.SaveChanges();

            result = this.Service.GetPossibleVenues(venues, false, users);

            Assert.IsTrue(result.Venues.Contains(this.PizzaPlace));
            Assert.IsTrue(result.Venues.Contains(this.Tacos));
            Assert.IsTrue(result.Venues.Contains(this.WaffleHouse));
            Assert.IsFalse(result.HappyUsers.Contains(this.UserCharlie));
            Assert.IsFalse(result.HappyUsers.Contains(this.UserBravo));
            Assert.IsTrue(result.HappyUsers.Contains(this.UserAlpha));
            Assert.IsTrue(result.UnhappyUsers.Contains(this.UserCharlie));
            Assert.IsTrue(result.UnhappyUsers.Contains(this.UserBravo));
            Assert.IsTrue(result.UnhappyUsers.Count() == 2);
            Assert.IsTrue(result.HappyUsers.Count() == 1);
        }

        [TestMethod]
        public void DontFilterVenuesEveryoneIsMehAbout()
        {
            var venues = new[] { this.WaffleHouse, this.Tacos, this.PizzaPlace };
            var users = new[] { this.UserAlpha, this.UserBravo, this.UserCharlie };

            this.UserAlpha.VenueDecisions.Add(new UserVenueChoice() { User = this.UserAlpha, Venue = this.WaffleHouse, Degree = DegreeOfPreference.WantToGo });
            this.UserBravo.VenueDecisions.Add(new UserVenueChoice() { User = this.UserBravo, Venue = this.WaffleHouse, Degree = DegreeOfPreference.WantToGo });
            this.UserCharlie.VenueDecisions.Add(new UserVenueChoice() { User = this.UserCharlie, Venue = this.WaffleHouse, Degree = DegreeOfPreference.WantToGo });

            this.DatabaseContext.SaveChanges();

            var result = this.Service.GetPossibleVenues(venues, false, users);

            Assert.IsTrue(result.Venues.Contains(this.PizzaPlace));
            Assert.IsTrue(result.Venues.Contains(this.Tacos));
            Assert.IsTrue(result.Venues.Contains(this.WaffleHouse));
            Assert.IsTrue(result.HappyUsers.Contains(this.UserCharlie));
            Assert.IsTrue(result.HappyUsers.Contains(this.UserAlpha));
            Assert.IsTrue(result.HappyUsers.Contains(this.UserBravo));
        }
    }
}
